#include "config.h"

#include <stdio.h>
#include <math.h>

#define DBG_SUBSYS S_LIBSTORAGE

#include "adt.h"
#include "sysy_lib.h"
#include "ytime.h"
#include "token_bucket.h"


int leaky_bucket_init(leaky_bucket_t *lb, uint64_t capacity, uint64_t rate)
{
        if (!lb->inited) {
                YASSERT(capacity > 0);
                YASSERT(rate > 0);

                lb->rate = rate;
                lb->capacity = capacity;
                lb->burst_max = lb->capacity;
                lb->water = 0;
                lb->t1 = ytime_gettime();

                lb->inited = 1;
        }
        return 0;
}

void leaky_bucket_destroy(leaky_bucket_t *lb)
{
        lb->inited = 0;
}

int leaky_bucket_set(leaky_bucket_t *lb, uint64_t capacity, uint64_t rate)
{
        YASSERT(capacity > 0);
        YASSERT(rate > 0);

        if (!lb->inited) {
                leaky_bucket_init(lb, capacity, rate);
        } else {
                lb->rate = rate;
                lb->capacity = capacity;
                lb->burst_max = lb->capacity;
                lb->water = 0;
                lb->t1 = ytime_gettime();
        }
        return 0;
}

int leaky_bucket_take(leaky_bucket_t *lb, suseconds_t now, int *is_ready, uint64_t *delay)
{
        *is_ready = 0;
        (void) delay;

        YASSERT(lb->inited);

        double numberToLeak = 1.0 * lb->rate * labs(now - lb->t1) / USECONDS_PER_SEC;
        if (numberToLeak > 0) {
                lb->water = _max(lb->water - numberToLeak, 0);
                lb->t1 = now;
        }

        if (lb->water + 1 <= lb->capacity) {
                lb->water++;
                *is_ready = 1;
        }

        DBUG("time %jd cap %ju rate %ju water %0.2f leak %0.2f ready %d\n",
              lb->t1, lb->capacity, lb->rate, lb->water, numberToLeak, *is_ready);

        return 0;

}
